﻿// This source file is part of WallpaperControl, a tool for automatic changing 
// of the desktop wallpaper.
// 
// Visit http://wpcontrol.sourceforge.net/ for more.
//
// Copyright (c) 2014 nscrolldev
//
// Licensed under the GPLv2.
// See http://www.gnu.org/licenses/gpl-2.0.html for details.
//
// This program is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public License
// as published by the Free Software Foundation; either version 2
// of the License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.

using System;
using System.Collections.Generic;
using System.Windows.Forms;
using System.IO;
using System.Security;
using System.Diagnostics;
using System.ComponentModel;

namespace WallpaperControl
{
    /// <summary>
    /// Class that manages the list of images.
    /// </summary>
    sealed class ImageList
    {
        /// <summary>
        /// Holds the only instance of this class.
        /// </summary>
        private static readonly ImageList _instance = new ImageList();

        /// <summary>
        /// The list of images.
        /// </summary>
        private List<Wallpaper> _images;

        /// <summary>
        /// The original list of images. Used for restoring.
        /// </summary>
        private List<Wallpaper> _oldImages;

        /// <summary>
        /// Indicates whether the image list has been changed.
        /// </summary>
        private bool _isChanged;

        /// <summary>
        /// Creates a new ImageList by initializing the internal data structures.
        /// </summary>
        private ImageList()
        {
            this.Images = new List<Wallpaper>();
            this.OldImages = new List<Wallpaper>();
        }

        /// <summary>
        /// Get or sets the list of images.
        /// </summary>
        internal List<Wallpaper> Images
        {
            get
            {
                return this._images;
            }
            set
            {
                this._images = value;
            }
        }

        /// <summary>
        /// Gets the list of old images.
        /// </summary>
        internal List<Wallpaper> OldImages
        {
            get
            {
                return this._oldImages;
            }
            private set
            {
                this._oldImages = value;
            }
        }

        /// <summary>
        /// Determines whether the image list has been modified.
        /// </summary>
        private bool Changed
        {
            get
            {
                return this._isChanged;
            }
            set
            {
                this._isChanged = value;
            }
        }

        /// <summary>
        /// Gets the instance of the image list.
        /// </summary>
        public static ImageList Instance
        {
            get
            {
                return _instance;
            }
        }

        /// <summary>
        /// Adds one or more new images to the list. The images are selected by an OpenFileDialog.
        /// </summary>
        public void AddImage()
        {
            // Store original image list
            if (!this.Changed)
            {
                this.OldImages = this.Images;
            }
            // Mark list as changed
            this.Changed = true;

            string filePath;

            // Create dialog
            OpenFileDialog AddImageDialog = new OpenFileDialog();

            // Build file filter
            List<string> supportedExtensions = new List<string>();
            foreach (string ext in Program.SupportedImages)
            {
                supportedExtensions.Add("*" + ext);
            }

            // Filter supported files
            if (Program.HasAdvancedImgSupport())
            {
                AddImageDialog.Filter = Program.Lang.GetString("FileType_Images") + "|" + string.Join(";", supportedExtensions.ToArray());
            }
            else
            {
                AddImageDialog.Filter = Program.Lang.GetString("FileType_Bitmaps") + "|" + string.Join(";", supportedExtensions.ToArray());
            }

            // Dialog options
            AddImageDialog.Title = Program.Lang.GetString("Caption_AddImage");
            AddImageDialog.Multiselect = true;

            // If file has been selected in dialog
            if (AddImageDialog.ShowDialog() == DialogResult.OK)
            {
                try
                {
                    // Insert image(s) into the lists
                    foreach (string file in AddImageDialog.FileNames)
                    {
                        filePath = Path.GetFullPath(file);
                        // Use Add() for inserting at the end of the list
                        this.Images.Insert(0, new Wallpaper(filePath));
                        Program.ControlPanel.ListBoxImages.Items.Insert(0, filePath);
                    }
                }
                catch (SecurityException)
                {
                    MessageBox.Show(Program.Lang.GetString("Error_AddImage_Security"), Program.Lang.GetString("Caption_Error"), MessageBoxButtons.OK, MessageBoxIcon.Error);
                }
                catch (ArgumentOutOfRangeException)
                {
                    // TODO
                }
                catch (WallpaperFileException)
                {
                }
                catch (ImageFormatException)
                {
                }
                catch (ArgumentNullException)
                {
                }
                catch (NotSupportedException)
                {
                }
                catch (PathTooLongException)
                {
                }
            }
        }

        /// <summary>
        /// Adds a new image to the list from the file path.
        /// </summary>
        /// <param name="path">The path to the image file that should be added as a string.</param>
        public void AddImage(string path)
        {
            // Store original image list
            if (!this.Changed)
            {
                this.OldImages = this.Images;
            }
            // Mark image list as changed
            this.Changed = true;

            // Check that path is set
            if (String.IsNullOrEmpty(path))
                return;

            try
            {
                // Add new wallpaper with specified path to the top of the list
                this.Images.Insert(0, new Wallpaper(path));
                Program.ControlPanel.ListBoxImages.Items.Insert(0, path);
            }
            catch (ArgumentOutOfRangeException)
            {
                MessageBox.Show(Program.Lang.GetString("Error_Internal"), Program.Lang.GetString("Caption_InternalError"), MessageBoxButtons.OK, MessageBoxIcon.Error);
            }
            catch (WallpaperFileException)
            {
                MessageBox.Show(Program.Lang.GetString("Error_AddImage_NoPath"), Program.Lang.GetString("Caption_Error"), MessageBoxButtons.OK, MessageBoxIcon.Error);
            }
            catch (ImageFormatException)
            {
                // TODO
            }
        }

        /// <summary>
        /// Adds a new wallpaper to the list from a Wallpaper object.
        /// </summary>
        /// <param name="wallpaper">The wallpaper object to add.</param>
        /// <exception cref="WallpaperPathException">Thrown if the wallpaper path is invalid.</exception>
        public void AddImage(Wallpaper wallpaper)
        {
            // Store original image list
            if (!this.Changed)
            {
                this.OldImages = this.Images;
            }
            // Mark list as changed
            this.Changed = true;

            // TODO: Check that wallpaper is valid

            try
            {
                // Insert wallpaper at the top of the list
                this.Images.Insert(0, wallpaper);
                Program.ControlPanel.ListBoxImages.Items.Insert(0, wallpaper.Path);
            }
            catch (ArgumentOutOfRangeException)
            {
                MessageBox.Show(Program.Lang.GetString("Error_Internal"), Program.Lang.GetString("Caption_InternalError"), MessageBoxButtons.OK, MessageBoxIcon.Error);
            }
        }

        /// <summary>
        /// Removes the image selected in the listbox.
        /// </summary>
        public void RemoveImage()
        {
            // Store original image list
            if (!this.Changed)
            {
                this.OldImages = this.Images;
            }
            // Mark list as changed
            this.Changed = true;

            int deleteIndex;
            try
            {
                // Get index of selected image
                deleteIndex = Program.ControlPanel.ListBoxImages.SelectedIndex;
                // Remove from lists
                this.Images.RemoveAt(deleteIndex);
                // Remove from listbox
                Program.ControlPanel.ListBoxImages.Items.Remove(Program.ControlPanel.ListBoxImages.SelectedItem);
                // Select new image (item above)
                Program.ControlPanel.ListBoxImages.SelectedIndex = deleteIndex - 1;
            }
            catch (ArgumentException)
            {
                MessageBox.Show(Program.Lang.GetString("Error_RemoveImage_Argument"), Program.Lang.GetString("Caption_Error"), MessageBoxButtons.OK, MessageBoxIcon.Error);
            }
        }

        /// <summary>
        /// Removes all images in the list.
        /// </summary>
        public void RemoveAllImages()
        {
            // Store original image list
            if (!this.Changed)
            {
                this.OldImages = this.Images;
            }
            // Mark list as changed
            this.Changed = true;

            // Go through every item in the list
            int items = this.Images.Count;
            try
            {
                for (int i = 0; i < items; i++)
                {
                    // Remove current image from lists
                    this.Images.RemoveAt(0);
                    // Remove from listbox
                    Program.ControlPanel.ListBoxImages.Items.RemoveAt(0);

                }
            }
            catch (ArgumentOutOfRangeException)
            {
                // TODO: Messagebox
            }
            finally
            {
                // Keep interface up-to-date
                Program.ControlPanel.UpdateImageButtons();
            }
        }

        /// <summary>
        /// Opens the image with the associated program as a preview.
        /// </summary>
        public void PreviewImage()
        {
            string filePath;
            try
            {
                // Get path of selected image
                filePath = ImageList.Instance.Images[Program.ControlPanel.ListBoxImages.SelectedIndex].Path;
                
            }
            catch (FileNotFoundException)
            {
                MessageBox.Show(Program.Lang.GetString("Error_PreviewImage_NotFound"), Program.Lang.GetString("Caption_Error"), MessageBoxButtons.OK, MessageBoxIcon.Error);
            }
            catch (Win32Exception e)
            {
                MessageBox.Show(e.Message, Program.Lang.GetString("Caption_WindowsError"), MessageBoxButtons.OK, MessageBoxIcon.Error);
            }
            catch (ObjectDisposedException)
            {
                // TODO: Messagebox
            }
        }

        /// <summary>
        /// Moves the selected image up in the list.
        /// </summary>
        internal void MoveImageUp()
        {
            // Store original image list
            if (!this.Changed)
            {
                this.OldImages = this.Images;
            }
            // Mark image list as changed
            this.Changed = true;

            // Get index of selected image
            int index = Program.ControlPanel.ListBoxImages.SelectedIndex;
            // Get listbox item
            object listboxItem = Program.ControlPanel.ListBoxImages.SelectedItem;
            // Get image path
            Wallpaper imageItem = ImageList.Instance.Images[index];

            try
            {
                // The first image can't be moved up
                if (index > 0)
                {
                    // Remove image from listbox
                    Program.ControlPanel.ListBoxImages.Items.RemoveAt(index);
                    // Remove image from the lists
                    ImageList.Instance.Images.RemoveAt(index);
                    // Insert old item into listbox at its new position
                    Program.ControlPanel.ListBoxImages.Items.Insert(index - 1, listboxItem);
                    // Insert old item into lists at its new position
                    ImageList.Instance.Images.Insert(index - 1, imageItem);
                    // Keep the item selected
                    Program.ControlPanel.ListBoxImages.SelectedIndex = index - 1;
                }
            }
            catch (ArgumentException)
            {
                // TODO: Messagebox
            }
        }

        /// <summary>
        /// Moves the selected image down in the list.
        /// </summary>
        internal void MoveImageDown()
        {
            // Store original image list
            if (!this.Changed)
            {
                this.OldImages = this.Images;
            }
            // Mark image list as changed
            this.Changed = true;

            // Get index of selected image
            int index = Program.ControlPanel.ListBoxImages.SelectedIndex;
            // Get listbox item
            object listboxItem = Program.ControlPanel.ListBoxImages.SelectedItem;
            // Get image path
            Wallpaper imageItem = ImageList.Instance.Images[index];

            try
            {
                // Last item can't be moved down
                if (index < Program.ControlPanel.ListBoxImages.Items.Count - 1)
                {
                    // Remove item from listbox
                    Program.ControlPanel.ListBoxImages.Items.RemoveAt(index);
                    // Remove from lists
                    ImageList.Instance.Images.RemoveAt(index);
                    // Insert old item into listbox at new position
                    Program.ControlPanel.ListBoxImages.Items.Insert(index + 1, listboxItem);
                    // Insert old item into lists at new position
                    ImageList.Instance.Images.Insert(index + 1, imageItem);
                    // Keep item selected
                    Program.ControlPanel.ListBoxImages.SelectedIndex = index + 1;
                }
            }
            catch (ArgumentException)
            {
                // TODO
            }
        }

        /// <summary>
        /// Restores the unmodified image list.
        /// </summary>
        internal void RestoreImages()
        {
            // Check that original image list is available
            if (this.OldImages != null)
            {
                // Set image list to original list
                this.Images = this.OldImages;
                // Mark list as unchanged
                this.Changed = false;
            }
        }
    }
}
